﻿#pragma once

#include "common.h"
#include "ui/ui.h"
//#include "ui/button.h"
#include "ui/tabctrl.h"
#include "ui/resource.h"

namespace anip
{
	namespace ui
	{
		enum class AutoDockStyle
		{
			None, Left, Right, Top, Bottom,
			Center
		};

		// Do NOT inherit this class directly. Use AutoDockable<C> instead.
		class AutoDockableBase
		{
			friend class SplitContainer;
			friend class AutoDockContainer;

		private:
			Control* _container = nullptr;
			bool _tabbed = false;

		protected:
			virtual void processOnDocked() {}
			virtual void processOnUndocked() {}
			virtual wstring getTabTitle() const = 0;

		public:
			bool isDocked() const { return _container != nullptr; }
			bool isTabbed() const { return _tabbed; }
			Container* dockContainer() const { return (Container*)_container; }
		};

		template<class C>
		class AutoDockable : public C, public AutoDockableBase
		{
			anip_control_ornament_class;
			friend class SplitContainer;
			friend class AutoDockContainer;

			using C::C;
			
		public:
			using C::getText;

		private:
			virtual wstring getTabTitle() const override { return getText(); }
		};

		class SplitContainer : public AutoDockable<Control>
		{
		public:
			enum class Style
			{
				Horizontal, Vertical
			};

		private:
			float 
				_splitPos = 100, 
				_dragSplitPos = 0;

			vec2 
				_resizerPos{},
				_resizerSize{},
				_splitAxis{};

			Control
				* _ctrl1 = nullptr, 
				* _ctrl2 = nullptr;

			Style _style;

		protected:
			bool canBeginDrag(MouseButton btn, invec2 pos) override;
			void processDrag(invec2 start, invec2 pos) override;
			void processMouseMove(invec2 pos) override;
			void processEndDrag() override;

			// ANIP_FIXME: very awkward!
			void addChild(Control& child, bool top = false) override
			{
				ANIP_FAIL("Calling SplitContainer::addChild is invalid");
			}

			void insertChild(Control& child, Control& before) override
			{
				ANIP_FAIL("Calling SplitContainer::insertChild is invalid");
			}

			void flowChildren(Flow& f) override;

		public:
			bool autoDissolve = false;

			SplitContainer(Container* parent, Style s, 
				invec2 pos = { 0, 0 }, invec2 size = { 100, 100 });

			template<class C>
			void setControl1(AutoDockable<C>& ctrl)
			{
				assert(_ctrl1 == nullptr);
				if (ctrl._parent != nullptr)
					((Container*)ctrl._parent)->detachChild(ctrl);
				_ctrl1 = &ctrl;
				Control::addChild(ctrl);
				ctrl._container = this;
				postSignal({ ui::Signal::Type::NeedsReflow });
			}

			template<class C>
			void setControl2(AutoDockable<C>& ctrl)
			{
				assert(_ctrl2 == nullptr);
				if (ctrl._parent != nullptr)
					((Container*)ctrl._parent)->detachChild(ctrl);
				_ctrl2 = &ctrl;
				Control::addChild(ctrl);
				ctrl._container = this;
				postSignal({ ui::Signal::Type::NeedsReflow });
			}

			Control* control1() const { return _ctrl1; }
			Control* control2() const { return _ctrl2; }

			void replaceChild(Control& child, Control& repl) override;
			void detachChild(Control& c) override;
			
			void setSize(invec2 s) override;

			Style style() const { return _style; }
			void setStyle(Style s);

			float splitPosition() const { return _splitPos; }
			void setSplitPosition(float s);

			void dissolve();

			void paint() override;
		};

		class AutoDockContainer : public AutoDockable<Control>
		{
		private:
			AutoDockStyle _hint = AutoDockStyle::None;
			TabControl* _tab = nullptr;
			Control* _central = nullptr;

		protected:
			// ANIP_FIXME: very awkward!
			void addChild(Control& child, bool top = false) override
			{
				ANIP_FAIL("Calling SplitContainer::addChild is invalid");
			}

			void insertChild(Control& child, Control& before) override
			{
				ANIP_FAIL("Calling SplitContainer::insertChild is invalid");
			}

			void flowChildren(Flow& f) override;
			void processMouseButton(MouseButton btn, ButtonAction action) override;

		public:
			AutoDockContainer(Container* parent,
				invec2 pos = { 0, 0 }, invec2 size = { 100, 100 });

			AutoDockStyle setDockHint(vec2 pos);
			void noDockHint();

			template<class C>
			void dockControl(AutoDockable<C>& ctrl, AutoDockStyle style = AutoDockStyle::None);

			Control* centralControl() const { return _central; }

			void replaceChild(Control& child, Control& repl) override;
			void detachChild(Control& c) override;

			void paint() override;
		};
	}
}

template<class C>
void anip::ui::AutoDockContainer::dockControl(AutoDockable<C>& ctrl, AutoDockStyle style)
{
	postSignal({ Signal::Type::NeedsReflow });

	if (style == AutoDockStyle::Center)
	{
		if (ctrl.parent() != nullptr)
			((Container*)ctrl.parent())->detachChild(ctrl);

		if (_tab != nullptr)
		{
			_tab->addPage(ctrl, ctrl.getTabTitle());
			ctrl._tabbed = true;
			ctrl._container = this;
			ctrl.processOnDocked();
		}
		else if (_central == nullptr)
		{
			Control::addChild(ctrl);
			_central = &ctrl;
			ctrl._container = this;
			ctrl.processOnDocked();
		}
		else
		{
			auto& central = dynamic_cast<AutoDockableBase&>(*_central);

			_tab = new TabControl(nullptr);
			Control::replaceChild(*_central, *_tab);
			_tab->addPage(*_central, central.getTabTitle());
			_tab->addPage(ctrl, ctrl.getTabTitle());
			central._tabbed = true;
			ctrl._tabbed = true;
			ctrl._container = this;
			central.processOnDocked();
			ctrl.processOnDocked();
			_central = nullptr;
		}
		return;
	}

	auto split = new SplitContainer(nullptr,
		style == AutoDockStyle::Left || style == AutoDockStyle::Right
		? SplitContainer::Style::Horizontal
		: SplitContainer::Style::Vertical);
	((Container*)_parent)->replaceChild(*this, *split);
	split->layoutOptions = layoutOptions;
	split->setSize(_size);
	split->setPosition(_pos);

	auto adc = new AutoDockContainer(nullptr);
	adc->layoutOptions = layoutOptions;
	adc->dockControl(ctrl, AutoDockStyle::Center);

	if (style == AutoDockStyle::Left || style == AutoDockStyle::Top)
	{
		split->setControl1(*adc);
		split->setControl2(*this);
	}
	else
	{
		split->setControl1(*this);
		split->setControl2(*adc);
	}

	switch (style)
	{
	case AutoDockStyle::Left:
		split->setSplitPosition(_size.x * 0.3f); break;
	case AutoDockStyle::Top:
		split->setSplitPosition(_size.y * 0.3f); break;
	case AutoDockStyle::Right:
		split->setSplitPosition(_size.x * 0.7f); break;
	case AutoDockStyle::Bottom:
		split->setSplitPosition(_size.y * 0.7f); break;
	default:
		break;
	}

	split->autoDissolve = true;
	ctrl.processOnDocked();

	_hint = AutoDockStyle::None;
}